"""Delete PODs orphaned by gitlab-runner."""

import argparse
import datetime
import os
import pathlib

from cki_lib import chatbot
from cki_lib import misc
from cki_lib.gitlab import get_instance
from cki_lib.logger import get_logger
from cki_lib.session import get_session
import dateutil
import gitlab
from kubernetes import client
from kubernetes import config
import sentry_sdk

LOGGER = get_logger('cki.cki_tools.orphan_hunter')
SESSION = get_session('cki.cki_tools.orphan_hunter')


class GitLabRunnerPod:
    """Manage a pod spawned by gitlab-runner."""

    def __init__(self, api, pod):
        """Initialize the pod and parse environment variables."""
        self._api = api
        self._pod = pod
        self.name = self._pod.metadata.name

        env = {x.name: x.value
               for x in self._pod.spec.containers[0].env}
        self._gitlab = get_instance(f'https://{env["CI_SERVER_HOST"]}')
        self.project_path = env['CI_PROJECT_PATH']
        self._job_id = env['CI_JOB_ID']
        self.job_url = env['CI_JOB_URL']

    def job_status(self) -> str:
        """Check whether the GitLab job is still running."""
        job = self._get_job()
        if not job:
            # project exists, but job not? try again just to be sure
            job = self._get_job()
        if not job:
            return 'non-existing'
        if not job.finished_at:
            return 'not finished'
        now = datetime.datetime.now(datetime.timezone.utc)
        finished = dateutil.parser.parse(job.finished_at)
        # wait 5 minutes for the runner to clean up after itself
        if now - finished < datetime.timedelta(minutes=5):
            return 'not finished'
        return 'finished'

    def delete(self) -> None:
        """Delete the pod."""
        self._api.delete_namespaced_pod(name=self.name,
                                        namespace=self._pod.metadata.namespace,
                                        grace_period_seconds=0)

    def _get_job(self):
        """If the project exists, return the job or None; otherwise, throw."""
        project = self._gitlab.projects.get(self.project_path)
        try:
            return project.jobs.get(self._job_id)
        except gitlab.exceptions.GitlabGetError:
            return None


def notify_pod(pod, status: str, delete: bool) -> None:
    """Send a message to the chat bot."""
    action = 'Deleting' if delete else 'Detected'
    url = pod.job_url
    message = (f'👻 {action} abandoned pod <{url}|{pod.name}> from {status} job '
               f'of {pod.project_path}')
    chatbot.send_message(message)


def main(args: list[str] | None = None) -> None:
    """Delete PODs orphaned by gitlab-runner."""
    parser = argparse.ArgumentParser()
    parser.parse_args(args)

    LOGGER.info('Loading configuration')
    config.load_incluster_config()
    api = client.CoreV1Api()
    LOGGER.info('Getting pods')
    namespace = os.environ.get('ORPHAN_HUNTER_NAMESPACE') or pathlib.Path(
        '/var/run/secrets/kubernetes.io/serviceaccount/namespace'
    ).read_text(encoding='utf8')
    all_pods = api.list_namespaced_pod(namespace).items
    runner_pods = [p for p in all_pods if p.metadata.name.startswith('runner-')]
    LOGGER.info('Found %s gitlab-runner pods out of %s pods in total',
                len(runner_pods), len(all_pods))

    for runner_pod in runner_pods:
        with misc.only_log_exceptions():
            pod = GitLabRunnerPod(api, runner_pod)
            status = pod.job_status()
            LOGGER.info('Checking %s -> %s: %s', pod.name, pod.job_url, status)
            if status != 'not finished':
                delete = os.environ.get('ORPHAN_HUNTER_ACTION', 'report') == 'delete'
                notify_pod(pod, status, delete)
                if delete:
                    LOGGER.info('Deleting pod')
                    pod.delete()


if __name__ == '__main__':
    misc.sentry_init(sentry_sdk)
    main()
